/*
 * Cubem - Copyright (C) 2008 Ewan Davies
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 *
 */
package cubem;

import java.io.*;
import javax.microedition.rms.*;

/**
 *
 * @author Ewan
 */
public class Session {
    
    public Session(String puzzleName) {
        puzzle = puzzleName;
        solves = new Solve[INITIAL_SIZE];
        currIdx = 0;
    }
    
    private static final int INITIAL_SIZE = 10;
    private static final int INCREASE_SIZE = 5;
    private static final int HEADER_SIZE = 20;
    
    private int currIdx;
    private String puzzle;
    private Solve[] solves;
    
    public String getName() {
        return puzzle;
    }
    
    public int getLength() {
        return currIdx;
    }
    
    private long getAverage() {
        long total = 0;
        int numPops = 0, num = 0;
        
        for (int i = 0; i < currIdx; i++) {
            if (i == getBestSolveIndex() && getLength() > 2)
                continue;
            else if (i == getWorstSolveIndex() && getLength() > 2)
                continue;
            switch (solves[i].getPenalties()) {
                case Solve.PEN_NONE:
                    total += solves[i].getTime();
                    num++;
                    break;
                case Solve.PEN_POP:
                    numPops++;
                    total += solves[i].getTime();
                    num++;
                    break;
                case Solve.PEN_DNF:
                    numPops = 2;
                    break;
                case Solve.PEN_FOUL:
                    total += solves[i].getTime();
                    num++;
                    break;
            }
        }
        
        if (num == 0)
            return 0;
        else if (numPops > 1)
            return -1;
        else return total / num;
    }
    
    public String getAverageString() {
        long avg = getAverage();
        if (avg < 0) // DNF
            return "DNF";
        else
            return Solve.convertToTimeString(avg);
    }
    
    public Solve getSolve(int index) {
        return solves[index];
    }
    
    public Solve getBestSolve() {
        return solves[getBestSolveIndex()];
    }
    
    public int getBestSolveIndex() {
        long bestTime = Long.MAX_VALUE;
        int bestIdx = -1;
        long time;
        for (int i = 0; i < currIdx; i++) {
            time = solves[i].getTime();
            if (time > 0 && time < bestTime) {
                bestTime = time;
                bestIdx = i;
            }
        }
//        for (int i = 0; i < currIdx; i++) {
//            time = solves[i].getTime();
//            if (time < 0 && -time < bestTime) {
//                bestTime = -time;
//                bestIdx = i;
//            }
//        }
        return bestIdx;
    }
    
    public long getBestTime() {
        return getBestSolve().getTime();
/*        long bestTime = Long.MAX_VALUE;
        int bestIdx = -1;
        long time;
        for (int i = 0; i < currIdx; i++) {
            time = solves[i].getTime();
            if (time > 0 && time < bestTime) {
                bestTime = time;
                bestIdx = i;
            }
        }
        for (int i = 0; i < currIdx; i++) {
            time = solves[i].getTime();
            if (time < 0 && -time < bestTime) {
                bestTime = -time;
                bestIdx = i;
            }
        }
        return bestTime; */
    }
    
    public Solve getWorstSolve() {
        return solves[getWorstSolveIndex()];
    }
    
    public int getWorstSolveIndex() {
        long worstTime = 0;
        int worstIdx = -1;
        long time;
        for (int i = 0; i < currIdx; i++) {
            // if a DNF is reached, return the index to it
            if (solves[i].getPenalties() == Solve.PEN_DNF)
                return i;
            time = solves[i].getTime();
            if (time > 0 && time > worstTime) {
                worstTime = time;
                worstIdx = i;
            }
        }
//        for (int i = 0; i < currIdx; i++) {
//            time = solves[i].getTime();
//            if (time < 0 && -time > worstTime) {
//                worstTime = -time;
//                worstIdx = i;
//            }
//        }
        return worstIdx;
    }
    
    public long getWorstTime() {
        return getWorstSolve().getTime();
/*        long worstTime = 0;
        int worstIdx = -1;
        long time;
        for (int i = 0; i < currIdx; i++) {
            time = solves[i].getTime();
            if (time > 0 && time > worstTime) {
                worstTime = time;
                worstIdx = i;
            }
        }
        for (int i = 0; i < currIdx; i++) {
            time = solves[i].getTime();
            if (time < 0 && -time > worstTime) {
                worstTime = -time;
                worstIdx = i;
            }
        }
        return worstTime; */
    } 
    
    public void addSolve(Solve s) {
        solves[currIdx] = s;
        currIdx++;
        if (currIdx >= solves.length)
            increaseSolveArray();
    }
    
    public void addTime(String scramble, long time, int penalties) {
        solves[currIdx] = new Solve(scramble, time, penalties);
        currIdx++;
        if (currIdx >= solves.length)
            increaseSolveArray();
    }
    
    /**
     * Read session data from a recordstore
     */
    static Session readSession(String recordStoreName, int sessionIdx) {
//#ifdef SaveSession        
        RecordStore rs = null;
        Session s = null;
        ByteArrayInputStream bs;
        DataInputStream ds;
        int[] header;
        byte[] rec;
        int numEntries;
        /*
         * Read the settings from the recordstore
         */
        try {
            rs = RecordStore.openRecordStore(recordStoreName, false);
            numEntries = rs.getNumRecords();
            if (numEntries < 1) {
                rs.closeRecordStore();
                return null;
            }
            rec = new byte[rs.getRecordSize(1)];
            rec = rs.getRecord(1);
            header = readHeader(rec);
            int i = header[sessionIdx];
            
            rec = new byte[rs.getRecordSize(i)];
            rec = rs.getRecord(i++);
            bs = new ByteArrayInputStream(rec);
            ds = new DataInputStream(bs);
            s = new Session(ds.readUTF());
            ds.close();
            bs.close();
            do {
                rec = new byte[rs.getRecordSize(i)];
                rec = rs.getRecord(i++);
                s.addSolve(new Solve(rec));
            } while (i <= numEntries && rec != null);
           
            rs.closeRecordStore();
        } catch (Exception e) {
            try  {rs.closeRecordStore(); } catch (Exception err) {}
            e.printStackTrace();
        }
//#else
//#         s = null;
//#endif
        return s;
    }
    
    /**
     * Write a session data to a recordstore
     * 
     * Format:
     * First, an int[] header containing the indices to session beginnings
     * Session:
     * First, a session data record (puzzle name)
     * Then record(s) each with a serialized solves
     * Then a null record
     */
    public void writeSession(String recordStoreName) {
//#ifdef SaveSession        
        if (currIdx == 0)
            return;
        RecordStore rs = null;
        byte[] rec; int[] header;
        ByteArrayOutputStream bs = new ByteArrayOutputStream(200); // TODO add a const size field
        DataOutputStream ds = new DataOutputStream(bs); 
        int headerIndex;
        
        try {
            rs = RecordStore.openRecordStore(recordStoreName, true);
            
            // TODO: add multiple persistant sessions
            // For now, just delete any prevoiusly stored sessions + header
            int i = rs.getNumRecords();
            if (i > 0) {
                    rs.closeRecordStore();
                    RecordStore.deleteRecordStore(recordStoreName);
                    rs = RecordStore.openRecordStore(recordStoreName, true);
                }
        } catch (RecordStoreException rse) {
            try  {rs.closeRecordStore(); } catch (Exception err) {}
            rse.printStackTrace();
            return;
        }
        try {
            rec = rs.getRecord(1);
        } catch (RecordStoreException rse) {
            try {
                header = new int[HEADER_SIZE];
                for (int i = 0; i < HEADER_SIZE; i++) {
                    header[i] = 0;
                }
                writeHeader(header, rs);
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
        }
        try {
            // read header
            rec = new byte[rs.getRecordSize(1)];
            rec = rs.getRecord(1);
            header = readHeader(rec);
            for (headerIndex = 0; headerIndex < header.length; headerIndex++) {
                if (header[headerIndex] == 0)
                    break;
            }
            // add the session names record, and store id in the header
            ds.writeUTF(puzzle);
            rec = bs.toByteArray();
            header[headerIndex] = (byte)rs.addRecord(rec, 0, rec.length);
            ds.close();
            bs.close();
            // add solves
            for (int i = 0; i < currIdx; i++) {
                rec = getSolve(i).serialize();
                rs.addRecord(rec, 0, rec.length);
            }
            // add null record terminator
            rs.addRecord(null, 0, 0);
            // now rewrite header          
            writeHeader(header, rs);
            rs.closeRecordStore();
        } catch (Exception e) {
            try  {rs.closeRecordStore(); } catch (Exception err) {}
            e.printStackTrace();
        }
    }
    
    private static int[] readHeader(byte[] bytes) {
        int[] header = new int[HEADER_SIZE];
        ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
        DataInputStream di = new DataInputStream(bi);
        try {
            for (int i = 0; i < HEADER_SIZE; i++) {
                header[i] = di.readInt();
            }
            di.close();
            bi.close();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return header;
//#endif
    }
    
    private static void writeHeader(int[] header, RecordStore rs) 
            throws IOException, RecordStoreException {
        ByteArrayOutputStream bs = new ByteArrayOutputStream(4*HEADER_SIZE);
        DataOutputStream ds = new DataOutputStream(bs); 
        
        for (int i = 0; i < HEADER_SIZE; i++) {
            ds.writeInt(header[i]);
        }
        
        byte[] rec = bs.toByteArray();
        if (rs.getNumRecords() < 1)
            rs.addRecord(rec, 0, rec.length);
        else
            rs.setRecord(1, rec, 0, rec.length);
        ds.close();
        bs.close();
    }
    
    private void increaseSolveArray() {
        if (solves == null) {
            solves = new Solve[INCREASE_SIZE];
        } else {
            Solve newSolves[] = new Solve[solves.length + INCREASE_SIZE];
            System.arraycopy(solves, 0, newSolves, 0, solves.length);
            solves = newSolves;
        }
    }
    
}
